/* eslint-disable class-methods-use-this, no-param-reassign */
import _ from 'lodash';
import Channel from '../models/channel';
import Message from '../models/message';
import WaitList from '../models/waitList';
import rtmOutEvents from '../rtm/outgoing/eventTypes';
import componentContainer from '../../components/componentContainer';

const WAITING_LIST_PREFIX = 'waitingList:';

class WaitHandler {
  async list(user, customerGroup, page = 1, pageSize = 5, prefer = {}) {
    const redis = componentContainer.getRedis();
    const waitingListKey = WAITING_LIST_PREFIX + customerGroup.id;
    const startIdx = (page - 1) * pageSize;
    const endIdx = page * pageSize - 1; // eslint-disable-line

    let total = await redis.zcard(waitingListKey);
    let waitingList = await redis.zrange(waitingListKey, startIdx, endIdx);
    let channels = [];

    if (waitingList && waitingList.length) {
      channels = await Channel.find({ _id: { $in: waitingList } })
        .populate('group')
        .populate('lastCustomerMessage');
    } else {
      const queryParams = _.assign({}, prefer, { group: customerGroup });
      total = await WaitList.count(queryParams);
      waitingList = await WaitList.find(queryParams)
        .skip(startIdx)
        .limit(5)
        .sort({ createdAt: 1 })
        .populate({ path: 'channel', populate: { path: 'lastCustomerMessage' } });

      channels = waitingList.map((w) => {
        w.channel.service = w.service;
        return w.channel;
      });
    }

    const list = channels.map((channel) => {
      const message = channel.lastCustomerMessage;
      return {
        id: channel.id,
        name: channel.name,
        service: channel.service,
        message: message && {
          id: message.id,
          subType: message.subType,
          body: message.body,
          createAt: message.createdAt,
        },
      };
    });

    return {
      total,
      channels: list,
    };
  }

  async getLastMessage(chan) {
    return Message.findOne({
      toChannel: chan,
      subType: { $nin: ['event'] },
    }).sort('-createdAt');
  }

  async countByGroupId(groupId) {
    return WaitList.count({ group: groupId });
  }

  async add(chan, customerGroup) {
    const redis = componentContainer.getRedis();
    const event = rtmOutEvents.ADD_WAIT;

    if (!chan.populated('creator')) {
      await chan.populate('creator').execPopulate();
    }

    await WaitList.update({ channel: chan }, {
      group: customerGroup,
      service: chan.creator.service,
      user: chan.creator,
    }, { upsert: true });

    const lastMessage = await this.getLastMessage(chan);
    chan.lastCustomerMessage = lastMessage; // eslint-disable-line
    await chan.save();

    redis.publish(
      chan.group.getRedisGroupName(),
      JSON.stringify({
        event,
        payload: {
          group: customerGroup.name,
          channel: {
            id: chan._id,
            name: chan.name,
            service: chan.creator.service,
            message: lastMessage && {
              id: lastMessage._id,
              subType: lastMessage.subType,
              body: lastMessage.body,
              createAt: lastMessage.createdAt,
            },
          },
        },
      }),
    );
  }

  async remove(chan, customerGroup) {
    const redis = componentContainer.getRedis();
    const waitingListKey = WAITING_LIST_PREFIX + customerGroup.id;
    const event = rtmOutEvents.REMOVE_WAIT;

    await redis.zrem(waitingListKey, chan._id.toString());
    await WaitList.remove({ channel: chan });

    redis.publish(
      chan.group.getRedisGroupName(),
      JSON.stringify({
        event,
        payload: {
          group: customerGroup.name,
          channel: {
            id: chan._id,
            name: chan.name,
          },
        },
      }),
    );
  }
}

export default new WaitHandler();
